{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![Django Ledger](../django_ledger/static/django_ledger/logo_2/django_ledger_logo_dark@2x.png \"Django Ledger Logo\")"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "tags": []
   },
   "source": [
    "import os\n",
    "from datetime import date, datetime\n",
    "from random import randint, choices, random\n",
    "from zoneinfo import ZoneInfo\n",
    "\n",
    "import django\n",
    "# for easier visualization it is recommended to use pandas to render data...\n",
    "# if pandas is not installed, you may install it with this command: pip install -U pandas\n",
    "# pandas is not a dependency of django_ledger...\n",
    "from django.core.exceptions import ObjectDoesNotExist\n",
    "\n",
    "# Set your django settings module if needed...\n",
    "os.environ['DJANGO_SETTINGS_MODULE'] = 'dev_env.settings'\n",
    "\n",
    "# if using jupyter notebook need to set DJANGO_ALLOW_ASYNC_UNSAFE as \"true\"\n",
    "os.environ['DJANGO_ALLOW_ASYNC_UNSAFE'] = 'true'\n",
    "\n",
    "# change your working directory as needed...\n",
    "# os.chdir('../')\n",
    "\n",
    "django.setup()\n",
    "\n",
    "from django_ledger.models.entity import EntityModel\n",
    "from django_ledger.models.items import ItemModel\n",
    "from django_ledger.models.invoice import InvoiceModel\n",
    "from django_ledger.models.bill import BillModel\n",
    "from django_ledger.models.estimate import EstimateModel\n",
    "from django.contrib.auth import get_user_model\n",
    "from django_ledger.io import roles\n",
    "from django_ledger.io.io_library import IOBluePrint, IOLibrary\n",
    "\n",
    "try:\n",
    "    import pandas as pd\n",
    "\n",
    "    PANDAS_INSTALLED = True\n",
    "except ImportError:\n",
    "    PANDAS_INSTALLED = False"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Get Your Entity Administrator UserModel"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "tags": []
   },
   "source": [
    "# change this to your preferred django username...\n",
    "MY_USERNAME = 'ceo_user'\n",
    "MY_PASSWORD = 'NeverUseMe|VeryInsecure!'\n",
    "UserModel = get_user_model()\n",
    "\n",
    "try:\n",
    "    user_model = UserModel.objects.get(username__exact=MY_USERNAME)\n",
    "except ObjectDoesNotExist:\n",
    "    user_model = UserModel(username=MY_USERNAME)\n",
    "    user_model.set_password(MY_PASSWORD)\n",
    "    user_model.save()"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Get or Create an Entity Model"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "tags": []
   },
   "source": [
    "ENTITY_NAME = 'One Big Company, LLC'\n",
    "\n",
    "entity_model = EntityModel.create_entity(\n",
    "    name=ENTITY_NAME,\n",
    "    admin=user_model,\n",
    "    use_accrual_method=True,\n",
    "    fy_start_month=1\n",
    ")\n",
    "\n",
    "entity_model"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Chart of Accounts"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Create a Default Chart of Accounts\n",
    "- Newly created EntityModel do not have a default Code of Accounts yet.\n",
    "- Django Ledger support multiple chart of accounts, but only one can be assigned as default."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Check if entity has a default CoA"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "tags": []
   },
   "source": [
    "entity_model.has_default_coa()"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "code",
   "metadata": {
    "tags": []
   },
   "source": [
    "default_coa_model = entity_model.create_chart_of_accounts(\n",
    "    assign_as_default=True,\n",
    "    commit=True,\n",
    "    coa_name='My QuickStart CoA'\n",
    ")"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "code",
   "metadata": {
    "tags": []
   },
   "source": [
    "default_coa_model"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### EntityModel has now a Default Chart of Accounts"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "tags": []
   },
   "source": [
    "entity_model.has_default_coa()"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "code",
   "metadata": {
    "tags": []
   },
   "source": [
    "default_coa_model = entity_model.get_default_coa()\n",
    "default_coa_model"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Default Chart of Accounts is accessible from the Entity Model"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "collapsed": false,
    "jupyter": {
     "outputs_hidden": false
    }
   },
   "source": [
    "entity_model.default_coa == default_coa_model"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Populate Entity with Random Data (Optional)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If you are getting started with Django Ledger, you may want to populate an entity with random data to help you get familiar with the API."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Define a Start Date for Transactions"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "tags": []
   },
   "source": [
    "START_DTTM = datetime(year=2022, month=10, day=1, tzinfo=ZoneInfo('UTC'))"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false,
    "jupyter": {
     "outputs_hidden": false
    }
   },
   "source": [
    "### Fill the entity with random data.\n",
    "- This action will populate the EntityModel with random data.\n",
    "- It will populate a Code of Accounts using a default pre-defined list.\n",
    "- This approach is for illustration, educational and testing purposes, not encouraged for new production entities.\n",
    "- Only Entities with no transactions can use this method."
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "tags": []
   },
   "source": [
    "entity_model.populate_random_data(start_date=START_DTTM)"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Chart of Accounts (CoA)\n",
    "- A Chart of Accounts is a user-defined list of accounts. \n",
    "- Each Entity Model must have at least one default Chart of Accounts."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Django Ledger supports multiple chart of accounts."
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "tags": []
   },
   "source": [
    "another_coa_model = entity_model.create_chart_of_accounts(\n",
    "    assign_as_default=False,\n",
    "    commit=True,\n",
    "    coa_name='My Empty Chart of Accounts'\n",
    ")"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "code",
   "metadata": {
    "collapsed": false,
    "jupyter": {
     "outputs_hidden": false
    }
   },
   "source": [
    "another_coa_model"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Accounts"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false,
    "jupyter": {
     "outputs_hidden": false
    }
   },
   "source": [
    "## Default CoA Accounts\n",
    "- These accounts are automatically created when populating the Entity with random data."
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "tags": []
   },
   "source": [
    "default_coa_accounts_qs = entity_model.get_default_coa_accounts().values('code', 'name', 'role', 'balance_type',\n",
    "                                                                         'active', 'locked')\n",
    "pd.DataFrame(default_coa_accounts_qs) if PANDAS_INSTALLED else default_coa_accounts_qs\n"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Get CoA Accounts by Explicit CoA Model (In case of multiple CoAs)"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "tags": []
   },
   "source": [
    "coa_accounts_by_coa_model_qs = entity_model.get_coa_accounts(coa_model=default_coa_model).values('code', 'name', 'role',\n",
    "                                                                                                 'balance_type',\n",
    "                                                                                                 'active', 'locked')\n",
    "pd.DataFrame(coa_accounts_by_coa_model_qs) if PANDAS_INSTALLED else coa_accounts_by_coa_model_qs"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false,
    "jupyter": {
     "outputs_hidden": false
    }
   },
   "source": [
    "No Accounts yet on this CoA..."
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "collapsed": false,
    "jupyter": {
     "outputs_hidden": false
    }
   },
   "source": [
    "coa_accounts_by_coa_model_qs = entity_model.get_coa_accounts(coa_model=another_coa_model).values('code', 'name', 'role',\n",
    "                                                                                                 'balance_type',\n",
    "                                                                                                 'active', 'locked')\n",
    "pd.DataFrame(coa_accounts_by_coa_model_qs) if PANDAS_INSTALLED else coa_accounts_by_coa_model_qs"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Get CoA Accounts by CoA Model UUID\n",
    "- May pass UUID instance instead of ChartOF AccountsModel.\n",
    "- Useful when using the CoA model UUID as URL kwarg."
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "tags": []
   },
   "source": [
    "coa_accounts_by_coa_uuid_qs = entity_model.get_coa_accounts(coa_model=default_coa_model.uuid).values('code', 'name',\n",
    "                                                                                                     'role',\n",
    "                                                                                                     'balance_type',\n",
    "                                                                                                     'active', 'locked')\n",
    "pd.DataFrame(coa_accounts_by_coa_uuid_qs) if PANDAS_INSTALLED else coa_accounts_by_coa_uuid_qs"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Get CoA Accounts by CoA Model Slug\n",
    "- If string is passed, will lookup by slug.\n",
    "- Useful when using the CoA model UUID as URL kwarg."
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "tags": []
   },
   "source": [
    "coa_accounts_by_coa_slug_qs = entity_model.get_coa_accounts(coa_model=default_coa_model.slug).values('code', 'name',\n",
    "                                                                                                     'role',\n",
    "                                                                                                     'balance_type',\n",
    "                                                                                                     'active', 'locked')\n",
    "pd.DataFrame(coa_accounts_by_coa_slug_qs) if PANDAS_INSTALLED else coa_accounts_by_coa_slug_qs"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Get Accounts With Codes and CoA Model\n",
    "- Assumes default CoA if no coa_model is passed..."
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "tags": []
   },
   "source": [
    "coa_accounts_by_codes_qs = entity_model.get_accounts_with_codes(code_list=['1010', '1050']).values('code', 'name',\n",
    "                                                                                                   'role',\n",
    "                                                                                                   'balance_type',\n",
    "                                                                                                   'active', 'locked')\n",
    "pd.DataFrame(coa_accounts_by_codes_qs) if PANDAS_INSTALLED else coa_accounts_by_codes_qs"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false,
    "jupyter": {
     "outputs_hidden": false
    }
   },
   "source": [
    "Empty ChartOfAccountModel..."
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "collapsed": false,
    "jupyter": {
     "outputs_hidden": false
    }
   },
   "source": [
    "coa_accounts_by_codes_qs = entity_model.get_accounts_with_codes(\n",
    "    code_list=['1010', '1050'],\n",
    "    coa_model=another_coa_model\n",
    ").values('code', 'name', 'role', 'balance_type', 'active', 'locked')\n",
    "pd.DataFrame(coa_accounts_by_codes_qs) if PANDAS_INSTALLED else coa_accounts_by_codes_qs"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false,
    "jupyter": {
     "outputs_hidden": false
    }
   },
   "source": [
    "### Get All Accounts at Once\n",
    "- Convenience method to get all accounts from all chart of account models associated with the entity."
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "collapsed": false,
    "jupyter": {
     "outputs_hidden": false
    }
   },
   "source": [
    "coa_qs, coa_map = entity_model.get_all_coa_accounts()"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false,
    "jupyter": {
     "outputs_hidden": false
    }
   },
   "source": [
    "A dictionary, CoA Model -> Account List."
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "collapsed": false,
    "jupyter": {
     "outputs_hidden": false
    }
   },
   "source": [
    "coa_map.keys()"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "code",
   "metadata": {
    "collapsed": false,
    "jupyter": {
     "outputs_hidden": false
    }
   },
   "source": "coa_map[default_coa_model].values('code', 'name', 'role', 'balance_type', 'active', 'locked')",
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "code",
   "metadata": {
    "collapsed": false,
    "jupyter": {
     "outputs_hidden": false
    }
   },
   "source": "coa_map[another_coa_model].values('code', 'name', 'role', 'balance_type', 'active', 'locked')",
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Create Account Model\n",
    "- Creating AccountModel into empty \"another_coa_model\"..."
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "tags": []
   },
   "source": [
    "account_model = entity_model.create_account(\n",
    "    coa_model=another_coa_model,\n",
    "    code=f'1{str(randint(10000, 99999))}',\n",
    "    role=roles.ASSET_CA_INVENTORY,\n",
    "    name='A cool account created from the EntityModel API!',\n",
    "    balance_type=roles.DEBIT,\n",
    "    active=True,\n",
    "\n",
    "    # set as role default...\n",
    "    # is_role_default=True,\n",
    "    # force_role_default=True,\n",
    ")"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "code",
   "metadata": {
    "tags": []
   },
   "source": "account_model.role_default",
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "code",
   "metadata": {
    "tags": []
   },
   "source": [
    "another_coa_accounts_qs = entity_model.get_coa_accounts(coa_model=another_coa_model).values('code', 'name', 'role',\n",
    "                                                                                            'balance_type', 'active',\n",
    "                                                                                            'locked')\n",
    "pd.DataFrame(another_coa_accounts_qs) if PANDAS_INSTALLED else another_coa_accounts_qs"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false,
    "jupyter": {
     "outputs_hidden": false
    }
   },
   "source": [
    "# Basic Django Ledger Usage\n",
    "- The LedgerModel enables object-oriented programming into the accounting system.\n",
    "- Examples:\n",
    "    - A month.\n",
    "    - A customer.\n",
    "    - A vendor.\n",
    "    - A project.\n",
    "    - A building.\n",
    "- The more appropriate ledgers are created, the more segregation and control over transactions is possible."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Creating an Empty Ledger using the Entity API."
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "collapsed": false,
    "jupyter": {
     "outputs_hidden": false
    }
   },
   "source": [
    "ledger_model = entity_model.create_ledger(name='My October 2023 Ledger', posted=True)"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Creating Transaction Libraries"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "source": [
    "library = IOLibrary(name='quickstart-library')"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Create and Register a BluePrint"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "source": [
    "@library.register\n",
    "def sale_blueprint(\n",
    "        sale_amount,\n",
    "        contribution_margin_percent: float,\n",
    "        description: str = None\n",
    ") -> IOBluePrint:\n",
    "    blueprint = IOBluePrint()\n",
    "    cogs_amount = (1 - contribution_margin_percent) * sale_amount\n",
    "    blueprint.debit(account_code='1010', amount=sale_amount, description=description)\n",
    "    blueprint.credit(account_code='4010', amount=sale_amount, description=description)\n",
    "    blueprint.credit(account_code='1200', amount=cogs_amount, description=description)\n",
    "    blueprint.debit(account_code='5010', amount=cogs_amount, description=description)\n",
    "    return blueprint"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Get a Cursor"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "source": [
    "cursor = library.get_cursor(entity_model=entity_model, user_model=user_model)"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Dispatch Some Instructions"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "source": [
    "cursor.dispatch('sale_blueprint',\n",
    "                ledger_model='ledger-test-1',\n",
    "                sale_amount=120.345,\n",
    "                contribution_margin_percent=0.25,\n",
    "                description='so cool')\n",
    "cursor.dispatch('sale_blueprint',\n",
    "                ledger_model='ledger-test-1',\n",
    "                sale_amount=12.345,\n",
    "                contribution_margin_percent=0.2,\n",
    "                description='so cool')\n",
    "cursor.dispatch('sale_blueprint',\n",
    "                ledger_model=ledger_model,\n",
    "                sale_amount=34.455,\n",
    "                contribution_margin_percent=0.13,\n",
    "                description='so cool')\n",
    "cursor.dispatch('sale_blueprint',\n",
    "                ledger_model='ledger-test-12',\n",
    "                sale_amount=90.43,\n",
    "                contribution_margin_percent=0.17,\n",
    "                description='so cool')"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Commit Your Instructions\n",
    "Not recommended to post both ledger and journal entries. Posted transactions will immediately affect the books.\n",
    "**result** contains the resulting ledger models, journal entries and transactions. "
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "source": [
    "result = cursor.commit(\n",
    "    post_new_ledgers=True,\n",
    "    post_journal_entries=True,\n",
    "    je_timestamp='2023-12-02 12:00'\n",
    ")"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "code",
   "metadata": {},
   "source": [
    "result"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false,
    "jupyter": {
     "outputs_hidden": false
    }
   },
   "source": [
    "### Get Financial Statement Report Data fro Ledger Model"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false,
    "jupyter": {
     "outputs_hidden": false
    }
   },
   "source": [
    "Balance Sheet"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "collapsed": false,
    "jupyter": {
     "outputs_hidden": false
    }
   },
   "source": [
    "bs_data = ledger_model.digest_balance_sheet(\n",
    "    to_date=date(2023, 12, 31),\n",
    "    entity_slug=entity_model\n",
    ")\n",
    "\n",
    "bs_data.get_balance_sheet_data()"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false,
    "jupyter": {
     "outputs_hidden": false
    }
   },
   "source": [
    "Income Statement"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "collapsed": false,
    "jupyter": {
     "outputs_hidden": false
    }
   },
   "source": [
    "is_data = ledger_model.digest_income_statement(\n",
    "    from_date=date(2023, 1, 1),\n",
    "    to_date=date(2023, 12, 31),\n",
    "    entity_slug=entity_model\n",
    ")\n",
    "is_data.get_income_statement_data()"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false,
    "jupyter": {
     "outputs_hidden": false
    }
   },
   "source": [
    "Cash Flow Statement"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "collapsed": false,
    "jupyter": {
     "outputs_hidden": false
    }
   },
   "source": [
    "cfs_data = ledger_model.digest_cash_flow_statement(\n",
    "    from_date=date(2023, 1, 1),\n",
    "    to_date=date(2023, 12, 31),\n",
    "    entity_slug=entity_model\n",
    ")\n",
    "\n",
    "cfs_data.get_cash_flow_statement_data()"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false,
    "jupyter": {
     "outputs_hidden": false
    }
   },
   "source": [
    "All Statements in a Single Call"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "collapsed": false,
    "jupyter": {
     "outputs_hidden": false
    }
   },
   "source": [
    "fin_digest = ledger_model.digest_financial_statements(\n",
    "    from_date=date(2023, 1, 1),\n",
    "    to_date=date(2023, 12, 31),\n",
    "    entity_slug=entity_model\n",
    ")\n",
    "\n",
    "statement_data = fin_digest.get_financial_statements_data()"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "code",
   "metadata": {
    "collapsed": false,
    "jupyter": {
     "outputs_hidden": false
    }
   },
   "source": [
    "statement_data['balance_sheet']"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "code",
   "metadata": {
    "collapsed": false,
    "jupyter": {
     "outputs_hidden": false
    }
   },
   "source": [
    "statement_data['income_statement']"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "code",
   "metadata": {
    "collapsed": false,
    "jupyter": {
     "outputs_hidden": false
    }
   },
   "source": [
    "statement_data['cash_flow_statement']"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Customers"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Get Customers"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "tags": []
   },
   "source": [
    "customer_qs = entity_model.get_customers()\n",
    "pd.DataFrame(customer_qs.values()) if PANDAS_INSTALLED else customer_qs"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Create Customers"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "tags": []
   },
   "source": [
    "customer_model = entity_model.create_customer(customer_model_kwargs={\n",
    "    'customer_name': 'Mr. Big',\n",
    "    'description': 'A great paying customer!',\n",
    "})"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Vendors"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Get Vendors"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "tags": []
   },
   "source": [
    "vendor_qs = entity_model.get_vendors()\n",
    "pd.DataFrame(vendor_qs.values()) if PANDAS_INSTALLED else vendor_qs"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Create Vendor"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "tags": []
   },
   "source": [
    "vendor_model = entity_model.create_vendor(vendor_model_kwargs={\n",
    "    'vendor_name': 'ACME LLC',\n",
    "    'description': 'A Reliable Vendor!'\n",
    "})"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Invoices"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Get Invoices"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "tags": []
   },
   "source": [
    "invoices_qs = entity_model.get_invoices()\n",
    "pd.DataFrame(invoices_qs.values()) if PANDAS_INSTALLED else invoices_qs"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Create Invoice"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "tags": []
   },
   "source": [
    "invoice_model = entity_model.create_invoice(\n",
    "    customer_model='C-0000000006',\n",
    "    terms=InvoiceModel.TERMS_NET_30\n",
    ")"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "code",
   "metadata": {},
   "source": "invoice_model",
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Add Items to Invoices"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "tags": []
   },
   "source": [
    "invoices_item_models = invoice_model.get_item_model_qs()\n",
    "\n",
    "# K= number of items...\n",
    "K = 6\n",
    "\n",
    "invoice_itemtxs = {\n",
    "    im.item_number: {\n",
    "        'unit_cost': round(random() * 10, 2),\n",
    "        'quantity': round(random() * 100, 2),\n",
    "        'total_amount': None\n",
    "    } for im in choices(invoices_item_models, k=K)\n",
    "}\n",
    "\n",
    "# Choose operation ITEMIZE_APPEND to append itemtxs...\n",
    "invoice_itemtxs = invoice_model.migrate_itemtxs(itemtxs=invoice_itemtxs,\n",
    "                                                commit=True,\n",
    "                                                operation=InvoiceModel.ITEMIZE_REPLACE)\n",
    "invoice_itemtxs"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "code",
   "metadata": {
    "tags": []
   },
   "source": [
    "invoice_model.amount_due"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Bills"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Get Bills"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "tags": []
   },
   "source": [
    "bills_qs = entity_model.get_bills()\n",
    "pd.DataFrame(bills_qs.values()) if PANDAS_INSTALLED else bills_qs"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Create Bill"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "tags": []
   },
   "source": [
    "bill_model = entity_model.create_bill(\n",
    "    vendor_model='V-0000000002',\n",
    "    terms=BillModel.TERMS_NET_60\n",
    ")"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "code",
   "metadata": {
    "collapsed": false,
    "jupyter": {
     "outputs_hidden": false
    }
   },
   "source": [
    "bill_model"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Add Items to Bills"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "tags": []
   },
   "source": [
    "bill_item_models = bill_model.get_item_model_qs()\n",
    "\n",
    "K = 6\n",
    "\n",
    "bill_itemtxs = {\n",
    "    im.item_number: {\n",
    "        'unit_cost': round(random() * 10, 2),\n",
    "        'quantity': round(random() * 100, 2),\n",
    "        'total_amount': None\n",
    "    } for im in choices(bill_item_models, k=K)\n",
    "}\n",
    "\n",
    "# Choose operation ITEMIZE_APPEND to append itemtxs...\n",
    "bill_itemtxs = bill_model.migrate_itemtxs(itemtxs=bill_itemtxs,\n",
    "                                          commit=True,\n",
    "                                          operation=BillModel.ITEMIZE_REPLACE)\n",
    "\n",
    "bill_itemtxs"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "code",
   "metadata": {
    "tags": []
   },
   "source": [
    "bill_model.amount_due"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Purchase Orders"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Get Purchase Orders"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "tags": []
   },
   "source": [
    "purchase_orders_qs = entity_model.get_purchase_orders()\n",
    "pd.DataFrame(purchase_orders_qs.values()) if PANDAS_INSTALLED else purchase_orders_qs"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Create Purchase Order"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "tags": []
   },
   "source": [
    "po_model = entity_model.create_purchase_order()"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Add Items to Purchase Orders"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "tags": []
   },
   "source": [
    "po_item_models = po_model.get_item_model_qs()\n",
    "\n",
    "K = 6\n",
    "\n",
    "po_itemtxs = {\n",
    "    im.item_number: {\n",
    "        'unit_cost': round(random() * 10, 2),\n",
    "        'quantity': round(random() * 100, 2),\n",
    "        'total_amount': None\n",
    "    } for im in choices(po_item_models, k=K)\n",
    "}\n",
    "\n",
    "# Choose operation ITEMIZE_APPEND to append itemtxs...\n",
    "po_itemtxs = po_model.migrate_itemtxs(itemtxs=po_itemtxs,\n",
    "                                      commit=True,\n",
    "                                      operation=EstimateModel.ITEMIZE_REPLACE)\n",
    "\n",
    "po_itemtxs"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "code",
   "metadata": {
    "tags": []
   },
   "source": [
    "po_model.po_amount"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Estimates/Contracts"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Get Estimates/Contracts"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "tags": []
   },
   "source": [
    "estimates_qs = entity_model.get_estimates()\n",
    "pd.DataFrame(estimates_qs.values()) if PANDAS_INSTALLED else estimates_qs"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Create Estimate"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "tags": []
   },
   "source": [
    "estimate_model = entity_model.create_estimate(\n",
    "    estimate_title='A quote for new potential customer!',\n",
    "    customer_model='C-0000000009',\n",
    "    contract_terms=EstimateModel.CONTRACT_TERMS_FIXED\n",
    ")"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Add Items to Estimates"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "tags": []
   },
   "source": [
    "estimate_item_models = estimate_model.get_item_model_qs()\n",
    "\n",
    "K = 6\n",
    "\n",
    "estimate_itemtxs = {\n",
    "    im.item_number: {\n",
    "        'unit_cost': round(random() * 10, 2),\n",
    "        'unit_revenue': round(random() * 20, 2),\n",
    "        'quantity': round(random() * 100, 2),\n",
    "        'total_amount': None\n",
    "    } for im in choices(estimate_item_models, k=K)\n",
    "}\n",
    "\n",
    "# Choose operation ITEMIZE_APPEND to append itemtxs...\n",
    "estimate_itemtxs = estimate_model.migrate_itemtxs(itemtxs=estimate_itemtxs,\n",
    "                                                  commit=True,\n",
    "                                                  operation=EstimateModel.ITEMIZE_REPLACE)\n",
    "\n",
    "estimate_itemtxs"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "code",
   "metadata": {
    "tags": []
   },
   "source": [
    "estimate_model.get_cost_estimate()"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "code",
   "metadata": {
    "tags": []
   },
   "source": [
    "estimate_model.get_revenue_estimate()"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "code",
   "metadata": {
    "tags": []
   },
   "source": [
    "estimate_model.get_profit_estimate()"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "code",
   "metadata": {
    "tags": []
   },
   "source": [
    "estimate_model.get_gross_margin_estimate(as_percent=True)"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Bank Accounts"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Get Bank Accounts"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "source": [
    "bank_accounts_qs = entity_model.get_bank_accounts()\n",
    "pd.DataFrame(bank_accounts_qs.values()) if PANDAS_INSTALLED else bank_accounts_qs"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Create Bank Account"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "source": [
    "bank_account_model = entity_model.create_bank_account(name='A big bank account!',\n",
    "                                                      account_type='checking')"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Items"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Unit of Measures"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Get Unit of Measures"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "source": [
    "uom_qs = entity_model.get_uom_all()\n",
    "pd.DataFrame(uom_qs.values()) if PANDAS_INSTALLED else uom_qs"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Create a UOM"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "source": [
    "uom_model_ft = entity_model.create_uom(\n",
    "    name='Linear Feet',\n",
    "    unit_abbr='lin-ft'\n",
    ")"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Get Some UoMs"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "source": [
    "uom_model_unit = uom_qs.get(unit_abbr__exact='unit')\n",
    "uom_model_man_hr = uom_qs.get(unit_abbr__exact='man-hour')"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Expenses"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Get Expense Items"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "source": [
    "expenses_qs = entity_model.get_items_expenses()\n",
    "pd.DataFrame(expenses_qs.values()) if PANDAS_INSTALLED else expenses_qs"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Create Expense Item"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "source": [
    "expense_item_model = entity_model.create_item_expense(\n",
    "    name='Premium Pencils',\n",
    "    uom_model=uom_model_unit,\n",
    "    expense_type=ItemModel.ITEM_TYPE_MATERIAL\n",
    ")"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "code",
   "metadata": {},
   "source": [
    "expense_item_model.is_expense()"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Services"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Get Service Items"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "source": [
    "services_qs = entity_model.get_items_services()\n",
    "pd.DataFrame(services_qs.values()) if PANDAS_INSTALLED else services_qs"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Create Service Item"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "source": [
    "service_model = entity_model.create_item_service(\n",
    "    name='Yoga Class',\n",
    "    uom_model=uom_model_man_hr\n",
    ")"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "code",
   "metadata": {},
   "source": [
    "service_model.is_service()"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Products"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Get Product Items"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "source": [
    "products_qs = entity_model.get_items_products()\n",
    "pd.DataFrame(products_qs.values()) if PANDAS_INSTALLED else products_qs"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Create Product Items"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "source": [
    "product_model = entity_model.create_item_product(\n",
    "    name='1/2\" Premium PVC Pipe',\n",
    "    uom_model=uom_model_ft,\n",
    "    item_type=ItemModel.ITEM_TYPE_MATERIAL\n",
    ")"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "code",
   "metadata": {},
   "source": [
    "product_model.is_product()"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Inventory"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Get Inventory Items"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "source": [
    "inventory_qs = entity_model.get_items_inventory()\n",
    "pd.DataFrame(inventory_qs.values()) if PANDAS_INSTALLED else inventory_qs"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Create Inventory Items"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "source": [
    "inventory_model = entity_model.create_item_inventory(\n",
    "    name='A Home to Flip!',\n",
    "    uom_model=uom_model_unit,\n",
    "    item_type=ItemModel.ITEM_TYPE_LUMP_SUM\n",
    ")"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "code",
   "metadata": {},
   "source": [
    "inventory_model.is_inventory()"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Financial Statement PDF Reports"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Set Up\n",
    "- Must enable PDF support by installing dependencies via *pipenv*.\n",
    "    - pipenv install --categories pdf"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Balance Sheet"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "source": [
    "bs_report = entity_model.get_balance_sheet_statement(\n",
    "    to_date=date(2022, 12, 31),\n",
    "    save_pdf=True,\n",
    "    filepath='./'\n",
    ")\n",
    "# save_pdf=True saves the PDF report in the project's BASE_DIR.\n",
    "# filename and filepath may also be specified...\n",
    "# will raise not implemented error if PDF support is not enabled..."
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Balance Sheet Statement Raw Data"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "source": [
    "bs_report.get_report_data()"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Income Statement"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "source": [
    "ic_report = entity_model.get_income_statement(\n",
    "    from_date=date(2022, 1, 1),\n",
    "    to_date=date(2022, 12, 31),\n",
    "    save_pdf=True,\n",
    "    filepath='./'\n",
    ")\n",
    "# save_pdf=True saves the PDF report in the project's BASE_DIR.\n",
    "# filename and filepath may also be specified...\n",
    "# will raise not implemented error if PDF support is not enabled..."
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Income Statement Raw Data"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "scrolled": true
   },
   "source": [
    "ic_report.get_report_data()"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Cash Flow Statement"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "source": [
    "cf_report = entity_model.get_cash_flow_statement(\n",
    "    from_date=date(2022, 1, 1),\n",
    "    to_date=date(2022, 12, 31),\n",
    "    save_pdf=True,\n",
    "    filepath='./'\n",
    ")\n",
    "# save_pdf=True saves the PDF report in the project's BASE_DIR.\n",
    "# filename and filepath may also be specified..."
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Cash Flow Statement Raw Data"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "source": [
    "cf_report.get_report_data()"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## All Financial Statements Data in a single Call"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "source": [
    "reports = entity_model.get_financial_statements(\n",
    "    user_model=user_model,\n",
    "    from_date=date(2022, 1, 1),\n",
    "    to_date=date(2022, 12, 31),\n",
    "    save_pdf=True,\n",
    "    filepath='./'\n",
    ")\n",
    "# save_pdf=True saves the PDF report in the project's BASE_DIR.\n",
    "# filename and filepath may also be specified..."
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "code",
   "metadata": {},
   "source": [
    "reports.balance_sheet_statement.get_report_data()"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "code",
   "metadata": {},
   "source": [
    "reports.income_statement.get_report_data()"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "code",
   "metadata": {},
   "source": [
    "reports.cash_flow_statement.get_report_data()"
   ],
   "outputs": [],
   "execution_count": null
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
